Running processes in OS X as the logged-in user from outside the user’s account
One challenge that can crop up for Mac admins is the problem of running a script or other tool with root privileges and using it to launch and run another tool, script or application as if the logged-in user had launched it. An example of this would be installing Dropbox using an installer package, then launching the Dropbox application as the logged-in user as a post-installation task. One reason to do so would be to give the user the opportunity to sign into their Dropbox account.
To accomplish this task, Apple has provided functionality in the launchctl tool.
For 10.9.x and earlier, launchctl‘s bsexec function was used for this purpose. bsexec allows you to start a process like a tool, script or application in a specific context. One way to get the correct context for the logged-in user is to identify the process identifier (PID) for the loginwindow process associated with the logged-in user, which can be accomplished by using the command below:
ps auxww | grep "/System/Library/CoreServices/loginwindow.app/Contents/MacOS/loginwindow" | grep logged_in_username_here | grep -v "grep" | awk '{print $2}'
For example, if I logged in with an account named username, running the command shown below should provide the PID for the username account’s loginwindow process:
ps auxww | grep "/System/Library/CoreServices/loginwindow.app/Contents/MacOS/loginwindow" | grep username | grep -v "grep" | awk '{print $2}'
Once the PID has been identified, it can be used to identify the context that I want to start a process in. For example, the commands below can be run with root privileges to first identify the correct PID for the username account’s loginwindow process, then launch Safari as the username account using the open command:
ps auxww | grep "/System/Library/CoreServices/loginwindow.app/Contents/MacOS/loginwindow" | grep logged_in_username_here | grep -v "grep" | awk '{print $2}'
launchctl bsexec PID_number_here open "/Applications/Safari.app"
When we check the process list for which account is was launched from, the Safari process should show up as being run by the username account.
If you wanted to automate opening Safari as the logged-in user, you could run a script like the one below with root privileges:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# Identify the username of the logged-in user | |
logged_in_user=`python -c 'from SystemConfiguration import SCDynamicStoreCopyConsoleUser; import sys; username = (SCDynamicStoreCopyConsoleUser(None, None, None) or [None])[0]; username = [username,""][username in [u"loginwindow", None, u""]]; sys.stdout.write(username + "\n");'` | |
# Identify the loginwindow PID of the logged-in user | |
logged_in_user_loginwindow_pid=$(ps auxww | grep "/System/Library/CoreServices/loginwindow.app/Contents/MacOS/loginwindow" | grep "$logged_in_user" | grep -v "grep" | awk '{print $2}') | |
/bin/launchctl bsexec "$logged_in_user_loginwindow_pid" /usr/bin/open "/Applications/Safari.app" |
Starting in OS X Yosemite, Apple made a number of changes to the launchctl tool and added a new asuser function. The asuser function is designed to take the place of the bsexec function, in the context of starting processes in the context of a specific user account. This makes it easier, as you now just need to figure out the username and do not have to figure out the PID of the user’s loginwindow process.
You can identify the user by either the user’s account name, or by the account’s UID. One way to identify an account’s UID is to use the id command as shown below:
id -u username_goes_here
To continue with the example of opening Safari, you can run the commands below to first identify the correct UID, then launch Safari as the username account using the open command:
id -u username_goes_here
launchctl asuser username_uid_goes_here open "/Applications/Safari.app"
If you wanted to automate opening Safari using the logged-in user’s UID, you could run a script like the one below with root privileges:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# Identify the username of the logged-in user | |
logged_in_user=`python -c 'from SystemConfiguration import SCDynamicStoreCopyConsoleUser; import sys; username = (SCDynamicStoreCopyConsoleUser(None, None, None) or [None])[0]; username = [username,""][username in [u"loginwindow", None, u""]]; sys.stdout.write(username + "\n");'` | |
# Identify the UID of the logged-in user | |
logged_in_user_uid=`id -u "$logged_in_user"` | |
/bin/launchctl asuser "$logged_in_user_uid" /usr/bin/open "/Applications/Safari.app" |
As mentioned earlier, you can also use just the account name with launchctl‘s asuser function. In that case, you can run the command below to launch Safari as the username account using the open command:
launchctl asuser username_goes_here open "/Applications/Safari.app"
If you wanted to automate opening Safari using the logged-in user’s account name, you could run a script like the one below with root privileges:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# Identify the username of the logged-in user | |
logged_in_user=`python -c 'from SystemConfiguration import SCDynamicStoreCopyConsoleUser; import sys; username = (SCDynamicStoreCopyConsoleUser(None, None, None) or [None])[0]; username = [username,""][username in [u"loginwindow", None, u""]]; sys.stdout.write(username + "\n");'` | |
/bin/launchctl asuser "$logged_in_user" /usr/bin/open "/Applications/Safari.app" |
First of all thank you very much for all your articles and info. I learn a lot from them. Let’s see if this time I can contribute a little bit.
In your examples you use a python function to get the username of the logged in user inside a script that is run as root (sudo I suppose). As we are shell scripting and we are in bash, I would rather use other methods to get the actual username, mostly because they are shorter to write 😉
1) The first option would be to use
echo $USER”
because even if you use “sudo echo $USER” you get the username of the logged in user and not root. But this option only works inside a script run as sudo, but not a script that does “sudo su” or several concatenated “sudo” instances inside the script.
So, for simple scripts that are run from the logged in user as sudo it would work.
2) The second option would be to use “who am i” (yes, with spaces), which works from inside a script run as sudo or inside multiple concatenated sudos or “sudo su” shells. But to get the username only we would need to pass it to awk as
who am i | awk ‘{print $1}’
3) But the most elegant option I have found is
logname
which works in all instances and we don’t need to pass it to any other function to extract the username.
I have tried all three in bash and several versions of OS X and they seem to be available always (correct me if I am wrong). What I haven’t tried is to run them in a simple sh script (not bash).
My two cents
Great finding, thank you very much for that!
Thank you so very much for all your knowledge that you share. At least for me you have helped me a lot. Thanks again.